package manageflow

import (
	"encoding/binary"
	"strings"
	"sync"

	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/log"
	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/contract/native/manage/common"
	"hundsun.com/hsl/hschain/contract/native/manage/manageflow/exec"
	execcomm "hundsun.com/hsl/hschain/executor/common"
	"hundsun.com/hsl/hschain/protos/execute"
	protosledger "hundsun.com/hsl/hschain/protos/ledger"
	protosmanage "hundsun.com/hsl/hschain/protos/manage"
)

// Flow 管理流程接口
type Flow interface {
	stepNext() error
	stepPrev() error
}

// FlowInstance 流程实例
type FlowInstance struct {
	Status *protosmanage.ManageFlowStatus
	Nodes  []*Node

	StateMu *sync.RWMutex
}

// NewFlowInstance 新建流程实例
func NewFlowInstance(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction, flowName string, proposal *protosmanage.ManageFlowProposal) (*FlowInstance, error) {
	creator := gtx.GetTx().GetFrom()
	var f = &FlowInstance{
		Status: &protosmanage.ManageFlowStatus{
			Name:     flowName,
			Creator:  creator,
			Proposal: proposal,
			State:    protosmanage.ManageFlowState_InitFlowState,
		},
		StateMu: &sync.RWMutex{},
	}

	err := f.InitFlowID(ctx, gtx)
	if err != nil {
		log.Errorf("init flow id failed, err: %s", err.Error())
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_MANAGEFLOW_INIT_FAILED
		return nil, err
	}

	// 根据传入流程配置接在对应流程
	err = f.InitFromFlowType(ctx, gtx)
	if err != nil {
		log.Errorf("init flow instance from type failed, err: %s", err.Error())
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_MANAGEFLOW_INIT_FAILED
		return nil, err
	}
	return f, nil
}

// LoadFlowInstanceFromDB 从数据库加载历史管理流程
func LoadFlowInstanceFromDB(flowID uint32, ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) (*FlowInstance, error) {
	var flowInst = &FlowInstance{}
	flowStatusKey := common.GenStatusKey(flowID)

	nmFlowStatusRWStatus := &execute.RWStatus{Key: flowStatusKey, RwStatus: execute.RwStatusType_ReadStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, nmFlowStatusRWStatus)

	flowBytes, err := ctx.Get(flowStatusKey)
	if err != nil {
		log.Errorf("get manage flow status by key %s failed, err: %s\n", flowStatusKey, err.Error())
		return nil, err
	}
	var status = &protosmanage.ManageFlowStatus{}
	err = proto.Unmarshal(flowBytes, status)
	if err != nil {
		log.Errorf("unmarshal status info failed, err: %s", err.Error())
		return nil, err
	}
	flowInst.StateMu = &sync.RWMutex{}
	flowInst.Status = status

	err = flowInst.InitFromFlowType(ctx, gtx)
	if err != nil {
		log.Errorf("init from flow type failed, err: %s", err.Error())
		return nil, err
	}

	log.Infof("init flow instance from db successfully, flowID: %d", flowInst.GetCurrentFlowID())
	return flowInst, nil
}

// Init 初始化
func (f *FlowInstance) Init() error {
	return nil
}

// InitFlowID 初始化流程ID
func (f *FlowInstance) InitFlowID(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) error {
	manageFlowIDKey := common.GenIDKey()

	nmFlowIDRWStatus := &execute.RWStatus{Key: manageFlowIDKey, RwStatus: execute.RwStatusType_ReadStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, nmFlowIDRWStatus)

	flowIDBytes, err := ctx.Get(manageFlowIDKey)
	if err != nil && err != types.ErrNotFound {
		log.Errorf("get flow id from db failed, err: %s", err.Error())
		return err
	}
	if len(flowIDBytes) > 0 {
		lastFlowID := binary.BigEndian.Uint32(flowIDBytes)
		f.GetCurrentStatus().FlowID = lastFlowID + 1
	} else {
		f.GetCurrentStatus().FlowID = 0
	}

	return nil
}

// InitFromFlowType 通过流程类型加载流程配置
func (f *FlowInstance) InitFromFlowType(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) error {
	manageFlowCfgKey := common.GenCfgKey(f.GetCurrentStatus().Name)

	nmFlowCfgRWStatus := &execute.RWStatus{Key: manageFlowCfgKey, RwStatus: execute.RwStatusType_ReadStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, nmFlowCfgRWStatus)

	var flowCfg = &protosmanage.ManageFlowCfg{}
	cfgBytes, err := ctx.Get(manageFlowCfgKey)
	if len(cfgBytes) > 0 {
		err = proto.Unmarshal(cfgBytes, flowCfg)
		if err != nil {
			log.Errorf("unmarshal flow config info failed, err: %s", err.Error())
			return err
		}
	} else if err != nil && strings.EqualFold(f.GetCurrentStatus().Name, common.MainValidatorCfg) || strings.EqualFold(f.GetCurrentStatus().Name, common.GenesisValidatorCfg) {
		log.Infof("get config %s failed and use default main validator flow config", manageFlowCfgKey)
		flowCfg = common.GenDefaultMainValidatorFlowCfg(ctx.GetHeight())
		flowCfgKv, err := common.CfgKV(flowCfg)
		if err != nil {
			log.Errorf("generate cfg kv failed, err: %s", err.Error())
			return err
		}
		ctx.GetStateHandle().SetLocal(flowCfgKv.Key, flowCfgKv.Value)
	} else {
		log.Errorf("get config by cfgKey failed, err: %s", err.Error())
		return err
	}

	for step, nodeDef := range flowCfg.Nodes {
		createExec, err := exec.LoadExecution(nodeDef.GetExecName())
		if err != nil {
			return common.ErrUnknownExecutionType
		}
		node := NewNode(createExec(f.GetCurrentFlowID(), uint32(step), f.GetCurrentStatus().Proposal, nodeDef.GetProperties()), nodeDef.GetProperties())
		f.Nodes = append(f.Nodes, node)
	}
	return nil
}

// GetCurrentStatus 获取当前流程状态
func (f *FlowInstance) GetCurrentStatus() *protosmanage.ManageFlowStatus {
	return f.Status
}

// GetCurrentFlowID 获取当前flowID
func (f *FlowInstance) GetCurrentFlowID() uint32 {
	return f.GetCurrentStatus().GetFlowID()
}

// GetCurrentStep 获取当前step
func (f *FlowInstance) GetCurrentStep() uint32 {
	return f.GetCurrentStatus().GetStep()
}

// GetCurrentNode 获取当前执行节点
func (f *FlowInstance) GetCurrentNode() *Node {
	return f.Nodes[f.GetCurrentStep()]
}

// GetCurrentNodeID 获取当前所处节点ID
func (f *FlowInstance) GetCurrentNodeID() int {
	return int(f.GetCurrentStatus().GetCurrentNodeID())
}

// waitingExternal 需要等待外部消息出发
func (f *FlowInstance) waitingExternal() bool {
	return f.Status.State == protosmanage.ManageFlowState_WaitExternalFlowState
}

// updateState 更新flow状态
func (f *FlowInstance) updateState(state protosmanage.ManageFlowState) {
	if f.Status != nil {
		f.Status.State = state
	}
}

func (f *FlowInstance) updateStep() {
}

// execOver
func (f *FlowInstance) execOver() bool {
	return f.Status.GetState() == protosmanage.ManageFlowState_ExpiredFlowState ||
		f.Status.GetState() == protosmanage.ManageFlowState_WaitExternalFlowState ||
		f.Status.GetState() == protosmanage.ManageFlowState_SucceedFlowState ||
		f.Status.GetState() == protosmanage.ManageFlowState_ErrorFlowState
}

func (f *FlowInstance) execFinished() bool {
	return f.Status.GetState() == protosmanage.ManageFlowState_ExpiredFlowState ||
		f.Status.GetState() == protosmanage.ManageFlowState_SucceedFlowState ||
		f.Status.GetState() == protosmanage.ManageFlowState_ErrorFlowState
}

func (f *FlowInstance) execExternally() bool {
	if f.Nodes[f.GetCurrentStep()] != nil {
		return f.Nodes[f.GetCurrentStep()].properties.ExecExternally
	}
	return false
}

func (f *FlowInstance) alreadyExpired() bool {
	return f.Status.State == protosmanage.ManageFlowState_ExpiredFlowState
}

// Run 流程运行
func (f *FlowInstance) Run(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) error {
	log.Infof("current floww step :%d", f.GetCurrentStep())
	f.StateMu.Lock()
	defer f.StateMu.Unlock()
	if f.execFinished() {
		log.Infof("current flow %d already finished", f.GetCurrentStatus().GetFlowID())
		return nil
	}

	if ctx.GetHeight() < f.GetCurrentStatus().GetStartHeight() {
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_MANAGEFLOW_NOT_STARTED
		return common.ErrManageFlowNotStarted
	}

	if ctx.GetHeight() > f.GetCurrentStatus().GetEndHeight() {
		f.updateState(protosmanage.ManageFlowState_ExpiredFlowState)
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_MANAGEFLOW_EXPIRED
		return common.ErrManageFlowExpired
	}
	// 收到外部消息将状态修改为运行中，否则后续execOver会直接退出此次执行
	if f.waitingExternal() {
		f.updateState(protosmanage.ManageFlowState_RunningFLowState)
	}

	for !f.execOver() {
		err := f.StepStage(ctx, gtx)
		if err != nil {
			return err
		}
	}

	if f.alreadyExpired() {
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_EXPIRED
		return common.ErrManageFlowExpired
	}
	return nil
}

// StepStage 流程流转
func (f *FlowInstance) StepStage(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) (err error) {
	currentNode := f.GetCurrentNode()
	if currentNode == nil {
		return types.ErrNotFound
	}
	err = currentNode.Execute(ctx, gtx)
	if err != nil {
		f.stepPrev()

		if f.execExternally() {
			f.updateState(protosmanage.ManageFlowState_WaitExternalFlowState)
		} else {
			f.updateState(protosmanage.ManageFlowState_ErrorFlowState)
		}

		if err == common.ErrNotEnoughVotes {
			return nil
		}
		return err
	}
	if int(f.GetCurrentStep()+1) == len(f.Nodes) {
		f.GetCurrentStatus().EffectiveHeight = ctx.GetHeight()
		f.updateState(protosmanage.ManageFlowState_SucceedFlowState)
		f.updateFlowStatus()
		return nil
	}
	return f.stepNext(ctx, gtx)
}

func (f *FlowInstance) stepNext(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction) error {
	node := f.GetCurrentNode()
	if node == nil {
		return types.ErrNotFound
	}
	f.GetCurrentStatus().CurrentNodeID = node.nodeID
	if int(f.GetCurrentStep()+1) != len(f.Nodes) {
		f.GetCurrentStatus().Step++
		log.Debugf("flow step update from %d to %d", f.GetCurrentStep()-1, f.GetCurrentStep())
	}

	err := f.updateFlowStatus()
	if err != nil {
		return err
	}
	return f.StepStage(ctx, gtx)
}

func (f *FlowInstance) stepPrev() error {
	f.updateFlowStatus()

	if f.GetCurrentStep() == 0 { // 流程回到起始位置，注意起始位置为0,
		return nil
	}
	log.Debugf("flow step update from %d to %d", f.GetCurrentStep(), f.GetCurrentStatus().Step-1)
	f.GetCurrentStatus().Step--

	return nil
}

// saveFlowStatus 保存节点状态
func (f *FlowInstance) saveFlowStatus() error {
	return nil
}

// updateFlowStatus 更新节点状态
func (f *FlowInstance) updateFlowStatus() error {
	return nil
}
